<template>
  <!-- 分片上传重写 -->
  <div>
    <input type="file" ref="file" style="display:none" :accept="accept">
    <el-button type="primary" size="small" @click="selectFile">{{btnName}}</el-button>
  </div>
</template>

<script>
import request from "@/utils/ToolsClass/request"
import { hex_md5 } from './fileMd5.js';
export default {
  props:{
    // 绑定值对象
    model: {
      default: '',
    },
    // 文件上传约束类型
    accept:{
      type: String,
      default:"*"
    },
    // 按钮名称
    btnName:{
      type: String,
      default:"文件上传"
    },
    // 文件大小约束
    fileSize:{
      type: Number,
      default: 10*1024*1024
    },
    // 文件个数约束
    limit:{
      type: Number,
      default: 1
    },
    // 自定义文件校验规则
    fileValidate:{
      type: Function,
      default:()=>{ return true }
    },
  },
  data(){
    return{
      // 上传file标签的元素节点
      fileNode: '',
      // 存放文件列表，包括文件回显
      fileList: [],
      // 存放分片文件
      chunks: [],
      // 存放最后一片文件信息
      fileFinally: {},
      // 上传地址
      url: '/application/cqsys-application/sys/att/upload.do'
    }
  },
  methods:{
    // 点击打开文件选择
    selectFile(){
      this.$refs.file.click()
    },
    // 文件上传前事件
    selectFileBefore(){
      // 当选择文件为单选时
      let file = this.fileNode.files[0];
      // 校验文件大小
      if (file.size > this.fileSize) {
        this.$message.error('上传的文件大小已超过约束');
        return false;
      }
      // 校验文件格式
      if(this.accept != '*'){
        if (this.accept.indexOf(file.name.substring(file.name.lastIndexOf('.')).toLocaleLowerCase()) == -1) {
          this.$message.error('上传文件类型不在规定范围内！')
          return false
        }
      }
      // 自定义校验规则
      if(!this.fileValidate(file)){
        return false
      }
      // 校验规则结束以后进行文件上传工作
      this.fileSliceChunk(file, this.getFileMd5(file));
    },

    // 上传过程中的事件
    fileSliceChunk(file, md5){
      // 实现文件切片处理 「固定数量 & 固定大小」
      let max = 1024 * 100,
          count = Math.ceil(file.size / max),
          index = 0,
          chunks = [];
      if (count > 100) {
          max = file.size / 100;
          count = 100;
      }
      while (index < count) {
          chunks.push({
            // 附件id
            id: `WU_FILE_${index}`,
            // 分片后文件
            file: file.slice(index * max, (index + 1) * max),
            // 加密字符串
            md5,
            // 总分片
            chunks: count,
            // 当前分片
            chunk: index,
            // 当前分片大小
            chunkSize: max,
            // 文件路径
            filePath: '',
            // 文件名称
            name: file.name,
            // 文件类型
            type: file.type,
            // 最后一次文件更新时间
            lastModifiedDate: file.lastModifiedDate,
            // 文件总大小
            size: file.size
          });
          index++;
      }
      this.chunks = chunks;
      // 生成所有的分片集合以后发送请求
      this.fileUploader(chunks)
    },
    // 进行加密生成md5加密字符串
    getFileMd5(file){
      return hex_md5(file.name + file.size + file.ext + file.__hash);
    },
    // 发送分片上传请求
    fileUploader(chunks){
      let fileNode = chunks.shift();
      const exeFileUpload = (pro) => {
        if (!pro) return;
        // 发送action为1的分片确定
        this.actionOne(fileNode).then(res=>{
          if(res.code == '0'){
            // 发送action为2的上传分片
            this.actionTwo(fileNode).then(rjs=>{
              if(rjs.code == '0'){
                exeFileUpload(fileNode = chunks.shift())
                // 上传结束事件
                if (!fileNode) this.uploaderFinally(this.fileFinally);
              }else{
                this.uploaderError()
              }
            })
          }else{
            this.uploaderError()
          }
        }).catch(err=>{
          // 上传失败事件
          this.uploaderError(err)
        })
      }
      exeFileUpload(fileNode)
    },
    // 分片第一步(action：1)
    actionOne(file){
      let data = {
        action: '1',
        md5: file.md5,
        chunks: file.chunks,
        chunk: file.chunk,
        chunkSize: file.chunkSize,
        filePath: file.filePath
      }
      return request({
        url: this.url,
        method: 'post',
        data,
        headers:{
          'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
        }
      })
    },
    // 分片第二步(action:2)
    actionTwo(file){
      this.fileFinally = file;
      let data = {
        action: '2',
        ...file
      }
      return request({
        url: this.url,
        method: 'post',
        data,
        headers:{
          'Content-Type': 'multipart/form-data;'
        }
      }) 
    },
    // 上传结束事件(action:3)
    uploaderFinally(file){
      let data = {
        action: '3',
        md5: file.md5,
        fileName: file.name,
        chunks: file.chunks,
        mainId: this.mainId,
        mainType: this.mainType,
        filePath: file.filePath,
        fileSize: file.size,
      }
      request({
        url: this.url,
        method: 'post',
        data
      }).then(res=>{
        if(res.code == '0'){
          // 一种是直接修改当前绑定值
          this.limit == 1 ? this.$emit('update:model', this.fileList[0].id) : this.$emit('update:model', this.fileList.map(v=> v.id));
          // 一种是直接返回当前整个文件列表
          this.$emit('uploadCompleted', this.fileList)
        }
      })
    },
    // 上传失败事件
    uploaderError(err){
      // 当前模式下为单文件上传，所以会要求文件一个一个上传，所以如果上传出现问题，及删除列表最后一个文件即可
      this.fileList.pop();
    }
  },
  mounted(){
    this.fileNode = this.$refs.file
    this.fileNode.addEventListener('change',()=>{
      this.selectFileBefore()
    })
  },
  // 组件销毁时移除监听
  destroyed(){
    this.fileList = [];
    this.fileNode.addEventListener('change',()=>{})
  }
}
</script>